#include "MBStringMethods.h"
#include "MBDataTypes.h"

#include <algorithm>

using std::min;
using std::string;
using std::replace;
using std::vector;

/**
  * Finds the next end sequence in the string str (ASCII code <= 47 except 32
  * (blank), 43 (+), 45 (-) and 46 (.). If no end sequence is found str.npos is
  * returned.
  *
  * @param str string in which the end sequence is looked for
  * @param beginIndex first index under consideration
  */
string::size_type MB::FindNextEndSequence( const string &str, string::size_type beginIndex )
{
	string::size_type index = beginIndex;
	string::const_iterator it = str.begin();
	it += beginIndex;
	
	char c;

	for (; it != str.end(); it++)
	{
		c = *it;

		if (c <= 47 && c != 32 && c != 43 && c != 45 && c != 46)
			return index;

		index++;
	}

	return str.npos;
}

/**
  * Replaces all occurrences of a particular character inside a string and
  * returns the new string
  *
  * @param str string, that is used to replace one character of it
  * @param characterToReplace character, that is replaced inside str
  * @param newCharacter the new character, that is used instead every occurrence of characterToReplace
  * @return the new string, with every occurrence of characterToReplace replaced by newCharacter
  */
string MB::ReplaceCharacter( const string &str, const char &characterToReplace, const char &newCharacter )
{
	string result = str;
	replace( result.begin(), result.end(), characterToReplace, newCharacter );

	return result;
}

/**
  *	Tests, whether str ends with endString or not
  *
  * @param str string, that is used to find the endString at the end of it
  * @param endString string, which has to be found at the end of str to return true
  * @return true, when str ends with endString, false otherwise
  */
bool MB::StringEndsWith( const std::string &str, const std::string &endString )
{
	string::size_type length = endString.size();

	if (str.find( endString, str.size() - length ) == str.size() - length)
		return true;

	return false;
}

/**
  * Compares two strings and returns true when the strings are equal and the
  * case is ignored. Two strings "string" and "STRING" would be equal.
  *
  * @param first first string in the comparison
  * @param second second string in the comparison
  * @return true when the strings are equal and the case is ignored, false otherwise
  */
bool MB::StringEqualsStringIgnoreCase( const std::string &first, const std::string &second )
{
	MBuint size_first = first.size();
	
	if (second.size() != size_first)
		return false;
	
	for (MBuint i = 0; i < size_first; i++)
	{
		if (tolower( first[i] ) != tolower( second[i] ))
			return false;
	}

	return true;
}

/**
  * Returns true if first is smaller than second and the case is ignored
  *
  * @param first first string in the comparison
  * @param second second string in the comparison
  * @return true if first is smaller than second and the case is ignored, false otherwise
  */
bool MB::StringSmallerStringIgnoreCase( const std::string &first, const std::string &second )
{
	MBuint first_size = first.size();
	MBuint second_size = second.size();

	if (first_size == 0 && second_size > 0)
		return true;

	if (second_size == 0 && first_size > 0)
		return false;

	MBuint size = min( first.size(), second.size() );

	for (MBuint i = 0; i < size; i++)
	{
		if (tolower( first[i] ) < tolower( second[i] ))
			return true;
		else if (tolower( first[i] ) > tolower( second[i] ))
			return false;
	}

	return false;
}

/**
  *	Tests, whether str starts with startString or not
  *
  * @param str string, that is used to find the startString at the beginning of it
  * @param startString string, which has to be found at the beginning of str to return true
  * @return true, when str starts with startString, false otherwise
  */
bool MB::StringStartsWith( const string &str, const string &startString )
{
	if (str.find( startString ) == 0)
		return true;

	return false;
}

/**
  *	Tokenizes the string in str using the characters in delimiters to add
  * all the found tokens to the vector tokens.
  *
  * @param[in] str const reference to the string that is tokenized
  * @param[out] tokens pointer to the vector, that is used to add all the found tokens into
  * @param[in] delimiters const reference to a string, that contains all the characters that are used as delimiters
  */
void MB::Tokenize( const string &str, vector<string> *tokens,
	const string &delimiters )
{
	// Skip delimiters at beginning.
	string::size_type last_pos = str.find_first_not_of( delimiters, 0 );
	// Find first "non-delimiter".
	string::size_type pos = str.find_first_of( delimiters, last_pos );

	while (string::npos != pos || string::npos != last_pos)
	{
		// Found a token, add it to the vector.
		tokens->push_back( str.substr( last_pos, pos - last_pos ) );
		// Skip delimiters.  Note the "not_of"
		last_pos = str.find_first_not_of( delimiters, pos );
		// Find next "non-delimiter"
		pos = str.find_first_of( delimiters, last_pos );
	}
}